#ifndef C_GENERATE_DECLARATIONS_H__
#define C_GENERATE_DECLARATIONS_H__

#include "generate-basic.h"
#include "generate-type.h"
#include "header.h"

char* c$generateEnumDeclaration(struct EnumDeclaration* declaration)
{
	char* name = c$generateName(declaration->name);
	char* list = c$generateNameList(declaration->nameList);
	char* r = malloc(strlen(name) + strlen(list) + 9);
	sprintf(r,"enum %s{%s};",name,list);
	return r;
}

char* c$generateArgumentDeclaration(struct ArgumentDeclaration* argument)
{
	char* type = c$generateType(argument->type);
	char* name = c$generateName(argument->name);
	char* r = malloc(strlen(type) + strlen(name) + 2);
	sprintf(r,"%s %s",type,name);
	return r;
}

char* c$generateArgumentDeclarationList(
	struct ArgumentDeclarationList* arguments)
{
	if(arguments == NULL) return NULL;
	char* head = c$generateArgumentDeclaration(arguments->head);
	char* tail = c$generateArgumentDeclarationList(arguments->tail);

	if(tail == NULL) return head;
	assert(head != NULL);

	char* r = malloc(strlen(head) + strlen(tail) + 2);
	sprintf(r,"%s,%s",head,tail);
	return r;
}

char* c$generateFunctionDeclaration(struct FunctionDeclaration* declaration)
{
	char* type = c$generateType(declaration->type);
	char* name = c$generateName(declaration->name);
	char* arguments = c$generateArgumentDeclarationList(declaration->arguments);
	char* statement = c$generateStatement(declaration->statement);

	char* r = malloc(strlen(type) + strlen(name)
		+ strlen(arguments) + strlen(statement) + 6);
	sprintf(r,"%s %s(%s){%s}",type,name,arguments,statement);
	return r;
}

char* c$generateVariableDeclaration(struct VariableDeclaration* declaration)
{
	// TODO Implement initialization.
	assert(declaration->initialization == NULL);

	char* type = c$generateType(declaration->type);
	char* name = c$generateName(declaration->name);
	char* r = malloc(strlen(type) + strlen(name) + 3);
	sprintf(r,"%s %s;",type,name);
	return r;
}

char* c$generateVariableDeclarationList(
	struct VariableDeclarationList* declarationList)
{
	if(declarationList == NULL) return "";

	char* head = c$generateVariableDeclaration(declarationList->head);
	char* tail = c$generateVariableDeclarationList(declarationList->tail);
	char* r = malloc(strlen(head) + strlen(tail) + 1);
	sprintf(r,"%s%s",head,tail);
	return r;
}

char* c$generateStructDeclaration(struct StructDeclaration* declaration)
{
	char* name = c$generateName(declaration->name);
	char* list = c$generateVariableDeclarationList(declaration->list);

	assert(name != NULL);
	assert(list != NULL);

	char* r = malloc(strlen(name) + strlen(list) + 11);
	sprintf(r,"struct %s{%s};",name,list);
	return r;
}

char* c$generateUnionDeclaration(struct UnionDeclaration* declaration)
{
	char* name = c$generateName(declaration->name);
	char* list = c$generateVariableDeclarationList(declaration->list);

	assert(name != NULL);
	assert(list != NULL);

	char* r = malloc(strlen(name) + strlen(list) + 11);
	sprintf(r,"struct %s{%s};",name,list);
	return r;
}

char* c$generateDeclaration(struct Declaration* declaration)
{
	if(declaration == NULL) return NULL;

	switch(declaration->tag)
	{
		case ENUM_DECLARATION:
			return c$generateEnumDeclaration(declaration->declaration);
		case FUNCTION_DECLARATION:
			return c$generateFunctionDeclaration(declaration->declaration);
		case STRUCT_DECLARATION:
			return c$generateStructDeclaration(declaration->declaration);
		case UNION_DECLARATION:
			return c$generateUnionDeclaration(declaration->declaration);
		case VARIABLE_DECLARATION:
			return c$generateVariableDeclaration(declaration->declaration);
		default:
			assert(0); // Should never happen.
	}

	return NULL;
}

char* c$generateDeclarationList(struct DeclarationList* declarationList)
{
	if(declarationList == NULL) return NULL;
	char* head = c$generateDeclaration(declarationList->head);
	char* tail = c$generateDeclarationList(declarationList->tail);
	assert(head != NULL);

	if(tail == NULL) return head;

	char* r = malloc(strlen(head) + strlen(tail) + 1);
	sprintf(r,"%s%s",head,tail);
	return r;
}

#endif

